package com.cetcs.kmga.dataManager.schedule;

import com.cetc.cloud.kmga.util.DateUtils;
import com.cetcs.kmga.dataManager.dao.mybatis.FeatureCodeMapper;
import com.cetcs.kmga.dataManager.entity.device.vo.KeyAndValueVo;
import com.cetcs.kmga.dataManager.entity.global.AlarmCode;
import com.cetcs.kmga.dataManager.entity.sysCode.CodeCacheLog;
import com.cetcs.kmga.dataManager.global.DataManagerConstants;
import com.cetcs.kmga.dataManager.global.LogQueryEhcacheTimerTask;
import com.cetcs.kmga.dataManager.service.sysManager.SysManagerService;
import com.cetcs.kmga.dataManager.util.*;
import com.google.common.collect.Lists;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Table;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 初始化获取码表数据到内存数据库中
 *
 * @author xutao
 * @version V1.0 创建时间：2017-10-10 13:22
 * Copyright 2017 by CETC
 */
@Component
public class InitCdTypeListener implements ApplicationListener<ContextRefreshedEvent> {

    private static Log LOGGER = LogFactory.getLog(InitCdTypeListener.class);


    //时间间隔(一天)
    private static final long PERIOD_DAY = 3 * 60 * 60 * 1000;

    @Autowired
    private FeatureCodeMapper codeMapper;

    @Autowired
    private SysManagerService sysManagerService;

    @Autowired
    private IConsumerMqMsgOper consumerMqMsgOper;


    private final static String CODE_F_TB_CODE = "0";

    private final static String CODE_F_RESOURC = "1";

    private final static String CODE_F_DEVICE = "2";

    private final static String CODE_F_SYS_CLIENT = "3";


    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        //目的是屏蔽掉执行加载SpringMVC与Spring时执行两次的问题
        if (contextRefreshedEvent.getApplicationContext().getParent() == null) {
            try {
                TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
//                System.setProperty("user.timezone","Asia/Shanghai");
//                System.setProperty("user.timezone","GMT");
                List<List<String>> columnArr = initCdCacheTbColumns();
                DruidH2Pool.getInstance().createCacheTb(DataManagerConstants.INIT_CD_TYPE_TB, columnArr);
                LOGGER.info("==================init create code cache tb success ================");
                //开始装载码表数据
                createCodesIn2CacheTb();
                createResourceIn2CodeTb();
                createDeviceIn2CodeTb();
                createSysClientIn2CodeTb();
                sysManagerService.reloadSysEmailConf2Cache();
                LOGGER.info("==================初始化创建码表内存数据库成功 ================");
                //开启每天凌晨重载数据的定时器
                timingReloadCodeData();
                //开始开启监Mq监听器监听接受事件查看消息
                ConsumerMqUtil mqUtilThread = new ConsumerMqUtil();
                mqUtilThread.setQueueName(DataManagerConstants.NOTIFY_AUDIT_EVENT_V1);
                mqUtilThread.setMqMsgOper(consumerMqMsgOper);
                mqUtilThread.start();
                LOGGER.info("==================初始化开启MQ的监听接受事件线程 ================");
                //创建日志查询的定时任务，目的是定期清理数据的缓存
//                LogQueryEhcacheTimerTask ehcacheTimerTask = new LogQueryEhcacheTimerTask();
//                Timer timer = new Timer();
//                long min_1 = 60 * 1000;
//                long period = Long.parseLong(ReadConfigUtil.getValueByKey("conf/log-query.properties", "timer.period")) * min_1;
//                ehcacheTimerTask.setTimePir(period);
//                //定时线程2分钟之后执行第一次，之后没三十分钟执行一次
//                timer.schedule(ehcacheTimerTask, min_1 * 2, period);
                LOGGER.info("==================初始化开启定时清理内存中缓存的日志数据的线程================");
                this.initHbaseConn();
                LOGGER.error("==================初始化加载Hbase的连接================");

            } catch (Exception ex) {
                LOGGER.error("初始化创建码表内存数据库失败，失败原因：" + ex.getMessage(), ex);
            }
        }
    }

    private void timingReloadCodeData() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 1); //凌晨1点
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date date = calendar.getTime(); //第一次执行定时任务的时间
        //如果第一次执行定时任务的时间 小于当前的时间
        //此时要在 第一次执行定时任务的时间加一天，以便此任务在下个时间点执行。如果不加一天，任务会立即执行。
        if (date.before(new Date())) {
            date = this.addDay(date, 1);
        }
        Timer timer = new Timer();
        InitCdTimeTask initCdTimeTask = new InitCdTimeTask();
        //安排指定的任务在指定的时间开始进行重复的固定延迟执行。
//        timer.schedule(initCdTimeTask, date, PERIOD_DAY);
        timer.schedule(initCdTimeTask, PERIOD_DAY, PERIOD_DAY);
    }

    private Date addDay(Date date, int num) {
        Calendar startDT = Calendar.getInstance();
        startDT.setTime(date);
        startDT.add(Calendar.DAY_OF_MONTH, num);
        return startDT.getTime();
    }

    /**
     * 删除内存数据库中资源数据
     */
    private void removeResourceData(String isResource) {
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        try {
            conn = DruidH2Pool.getInstance().getConnection();
            String querySql = "DELETE from " + DataManagerConstants.INIT_CD_TYPE_TB;
            if (!StringUtils.isEmpty(isResource)) {
                querySql += " where isResource = ?";
            }
            preparedStatement = conn.prepareStatement(querySql);
            if (!StringUtils.isEmpty(isResource)) {
                preparedStatement.setString(1, isResource);
            }
            preparedStatement.executeUpdate();
            conn.commit();
        } catch (SQLException e) {
            LOGGER.error("删除码表资源数据出错" + e.getMessage(), e);
        } finally {
            try {
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                DruidH2Pool.getInstance().close(conn);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
    }

    private int queryResourceData() {
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        ResultSet rs = null;
        int totalRecord = 0;
        try {
            conn = DruidH2Pool.getInstance().getConnection();
            String querySql = "SELECT COUNT(*) from " + DataManagerConstants.INIT_CD_TYPE_TB;
            preparedStatement = conn.prepareStatement(querySql);
            rs = preparedStatement.executeQuery();
            while (rs.next()) {
                totalRecord += rs.getInt(1);
            }
        } catch (SQLException e) {
            LOGGER.error("删除码表资源数据出错" + e.getMessage(), e);
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                    rs = null;
                }
                if (preparedStatement != null) {
                    preparedStatement.close();
                    preparedStatement = null;
                }
                DruidH2Pool.getInstance().close(conn);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
        return totalRecord;
    }

    /**
     * 装载资源数据到码表内存数据库中
     */
    private int createCodesIn2CacheTb() {
        //开始装载资源数据为0
        List<AlarmCode> codes = codeMapper.findAllFeatureCodes();
        if (codes != null && codes.size() > 0) {
            List<List<String>> lists = codes.stream().map(p -> {
                List<String> singleList = Lists.newArrayList();
                singleList.add(p.getId() + "");
                singleList.add(p.getValue());
                singleList.add(p.getCode());
                singleList.add(p.getParentCode());
                singleList.add(p.getTypeId());
                //码表数据为0
                singleList.add(CODE_F_TB_CODE);
                return singleList;
            }).collect(Collectors.toList());
            DruidH2Pool.getInstance().createBathDataUseThread(DataManagerConstants.INIT_CD_TYPE_TB, initCdCacheTbColumnArr(), lists);
        }
        return codes.size();
    }

    /**
     * 装载资源数据到码表内存数据库中
     */
    private int createResourceIn2CodeTb() {
        //开始装载资源数据为1
        List<AlarmCode> resources = codeMapper.findAllResourceCodes();
        if (resources != null && resources.size() > 0) {
            List<List<String>> lists = resources.stream().map(p -> {
                List<String> singleList = Lists.newArrayList();
                singleList.add(p.getId() + "");
                singleList.add(p.getValue());
                singleList.add(p.getCode());
                singleList.add(p.getParentCode());
                singleList.add(p.getTypeId());
                //码表数据为1
                singleList.add(CODE_F_RESOURC);
                return singleList;
            }).collect(Collectors.toList());
            DruidH2Pool.getInstance().createBathDataUseThread(DataManagerConstants.INIT_CD_TYPE_TB, initCdCacheTbColumnArr(), lists);
        }
        return resources.size();
    }

    /**
     * 装载资源数据到码表内存数据库中
     */
    private int createDeviceIn2CodeTb() {
        //开始装载资源数据为1
        List<AlarmCode> devices = codeMapper.findAllDevices();
        String deviceKey = CdTypeCode.DEVICE.getCode();
        if (devices != null && devices.size() > 0) {
            List<List<String>> lists = devices.stream().map(p -> {
                List<String> singleList = Lists.newArrayList();
                singleList.add(p.getRowKey());
                singleList.add(p.getValue());
                singleList.add(p.getCode());
                singleList.add(p.getParentCode());
                singleList.add(deviceKey);
                //码表数据为1
                singleList.add(CODE_F_DEVICE);
                return singleList;
            }).collect(Collectors.toList());
            DruidH2Pool.getInstance().createBathDataUseThread(DataManagerConstants.INIT_CD_TYPE_TB, initCdCacheTbColumnArr(), lists);
        }
        return devices.size();
    }


    /**
     * 装载资源数据到码表内存数据库中
     */
    private int createSysClientIn2CodeTb() {
        //开始装载资源数据为1
        List<KeyAndValueVo> sysClientClusters = codeMapper.findAllSysClientClusters();
        if (sysClientClusters != null && sysClientClusters.size() > 0) {
            String rowKeyStart = CdTypeCode.SYS_CLIENT.getCode();
            MD5 md5 = new MD5();
            List<List<String>> lists = sysClientClusters.stream().map(p -> {
                List<String> singleList = Lists.newArrayList();
                String rStr = rowKeyStart + p.getKey() + p.getValue() + DateUtils.getTime() + UUID.randomUUID().toString();
                String md5Str = md5.getMD5ofStr(rStr);
                singleList.add(md5Str);
                singleList.add(p.getValue());
                singleList.add(p.getKey());
                singleList.add("");
                singleList.add(rowKeyStart);
                //码表数据为1
                singleList.add(CODE_F_SYS_CLIENT);
                return singleList;
            }).collect(Collectors.toList());
            DruidH2Pool.getInstance().createBathDataUseThread(DataManagerConstants.INIT_CD_TYPE_TB, initCdCacheTbColumnArr(), lists);
        }
        return sysClientClusters.size();
    }

    /**
     * 重载码表数据到内存数据库中，
     * 此方法用于mysql中的资源发生改变之后重新刷新资源使用
     * 先删除资源然后重新导入
     */
    public void reloadResourceIn2CodeTb() {
        removeResourceData(CODE_F_RESOURC);
        createResourceIn2CodeTb();
    }

    /**
     * 重载码表数据到内存数据库中，
     * 此方法用于mysql中的设备发生改变之后重新刷新资源使用
     * 先删除设备然后重新导入
     */
    public void reloadDevicesIn2CodeTb() {
        removeResourceData(CODE_F_DEVICE);
        createDeviceIn2CodeTb();
    }

    /**
     * 重载码表数据到内存数据库中，
     * 此方法用于mysql中的设备发生改变之后重新刷新资源使用
     * 先删除客户端然后重新导入
     */
    public void reloadSysClenitCodeTb() {
        removeResourceData(CODE_F_DEVICE);
        createSysClientIn2CodeTb();
    }

    /**
     * 组装初始化的内存数据库字段以及长度
     *
     * @return
     */
    private List<List<String>> initCdCacheTbColumns() {
        List<List<String>> columnArr = Lists.newArrayList();
        List<String> single = Lists.newArrayList();
        single.add("id");
        single.add("100");
        single.add("VARCHAR");
        columnArr.add(single);
        single = Lists.newArrayList();
        single.add("value");
        single.add("100");
        single.add("VARCHAR");
        columnArr.add(single);
        single = Lists.newArrayList();
        single.add("code");
        single.add("100");
        single.add("VARCHAR");
        columnArr.add(single);
        single = Lists.newArrayList();
        single.add("parentCode");
        single.add("100");
        single.add("VARCHAR");
        columnArr.add(single);
        single = Lists.newArrayList();
        single.add("typeId");
        single.add("100");
        single.add("VARCHAR");
        columnArr.add(single);
        //是否为资源数据的字段 判断是否为资源数据 值为：1：是资源数据；0：不是资源数据
        single = Lists.newArrayList();
        single.add("isResource");
        single.add("10");
        single.add("VARCHAR");
        columnArr.add(single);
        return columnArr;
    }


    /**
     * 组装初始化的内存数据库字段
     *
     * @return
     */
    private List<String> initCdCacheTbColumnArr() {
        List<String> single = Lists.newArrayList();
        single.add("id");
        single.add("value");
        single.add("code");
        single.add("parentCode");
        single.add("typeId");
        single.add("isResource");
        return single;
    }


    /**
     * 重载码表数据的方法
     */
    private class InitCdTimeTask extends TimerTask {
        @Override
        public void run() {
            LOGGER.error("========" + DateUtils.getTime() + "==========开始执行定时重载码表数据的任务 ================");
            reloadAllCodes(true, 0, DateUtils.getTime(), 1);
        }
    }

    private void reloadAllCodes(boolean isExe, int extTime, String startTime, int status) {

        if (isExe) {
            removeResourceData(null);
            int ret = createCodesIn2CacheTb();
            ret += createResourceIn2CodeTb();
            ret += createDeviceIn2CodeTb();
            ret += createSysClientIn2CodeTb();
            int queryOut = queryResourceData();
            extTime++;
            if (queryOut != ret && extTime < 6) {
                status = 0;
                reloadAllCodes(true, extTime, startTime, status);
            }
            CodeCacheLog cacheLog = new CodeCacheLog();
            cacheLog.setStartTime(startTime);
            cacheLog.setExeTime(extTime);
            cacheLog.setStatus(status);
            cacheLog.setInNum(ret);
            cacheLog.setOutNum(queryOut);
            cacheLog.setEndTime(DateUtils.getTime());
            codeMapper.createCacheCodeLog(cacheLog);

        }
    }

    private void initHbaseConn() throws IOException {
        Table table = HBasePool.getInstance().getConnection().getTable(TableName.valueOf("KMGA:SDATA_AUDIT_DB_LOG_201709"));
        if (table != null) {
            table.close();
        }
    }
}
